open Expr
  
let last_comment = ref None

type error_msg =
  | Unexpected of token | Unclosed_parenthesis | Duplicate_default

exception Error of error_msg * pos
  
let warning = ref (fun (msg : string) (p : pos) -> ())
  
let use_components = ref false
  
let error_msg =
  function
  | Unexpected t -> "Unexpected " ^ (s_token t)
  | Unclosed_parenthesis -> "Unclosed parenthesis"
  | Duplicate_default -> "Duplicate default"
  
let error m p = raise (Error (m, p))
  
let priority =
  function
  | OpAssign | OpAssignOp _ -> (-4)
  | OpBoolOr -> (-3)
  | OpBoolAnd -> (-2)
  | OpEq | OpNotEq | OpGt | OpLt | OpGte | OpLte | OpPhysEq | OpPhysNotEq ->
      (-1)
  | OpOr | OpAnd | OpXor -> 0
  | OpShl | OpShr | OpUShr -> 1
  | OpAdd | OpSub -> 2
  | OpMult | OpDiv -> 3
  | OpMod -> 4
  
let is_not_assign = function | OpAssign | OpAssignOp _ -> false | _ -> true
  
let can_swap _op op =
  let p1 = priority _op in
  let p2 = priority op
  in
    if p1 < p2
    then true
    else
      if (p1 = p2) && (p1 >= 0)
      then (* numerical ops are left-assoc *) true
      else false
  
let rec make_binop op e (((v, p2) as e2)) =
  match v with
  | EBinop (_op, _e, _e2) when
      (can_swap _op op) && ((is_not_assign _op) || (is_not_assign op)) ->
      let _e = make_binop op e _e
      in ((EBinop (_op, _e, _e2)), (punion (pos _e) (pos _e2)))
  | EQuestion (_e, _e1, _e2) when is_not_assign op ->
      let _e = make_binop op e _e
      in ((EQuestion (_e, _e1, _e2)), (punion (pos e) (pos _e2)))
  | _ -> ((EBinop (op, e, e2)), (punion (pos e) (pos e2)))
  
let rec make_unop op (((v, p2) as e)) p1 =
  match v with
  | EBinop (bop, e, e2) ->
      ((EBinop (bop, make_unop op e p1, e2)), (punion p1 p2))
  | EQuestion (_e, _e1, _e2) ->
      let _e = make_unop op _e p1
      in ((EQuestion (_e, _e1, _e2)), (punion p1 (pos _e2)))
  | _ -> ((EUnop (op, Prefix, e)), (punion p1 p2))
  
let rec make_path e =
  let rec loop acc (e, _) =
    match e with
    | EConst (Ident s) -> s :: acc
    | EField (e, f) -> loop (f :: acc) e
    | _ -> raise Stream.Failure
  in loop [] e
  
let wrap_var e = match e with | (EVars _, p) -> ((EBlock [ e ]), p) | _ -> e

let rec parse_fun_name (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Kwd k, p)) when (Filename.basename p.pfile) = "TopLevel.as" ->
      (Stream.junk __strm; ((s_keyword k), Normal))
  | Some ((Const (Ident name), _)) ->
      (Stream.junk __strm;
       let s = __strm
       in
         (match name with
          | "get" | "set" ->
              let (__strm : _ Stream.t) = s
              in
                (match Stream.peek __strm with
                 | Some ((Const (Ident name2), _)) ->
                     (Stream.junk __strm;
                      (name2, (if name = "get" then Getter else Setter)))
                 | _ -> (name, Normal))
          | _ -> (name, Normal)))
  | _ -> raise Stream.Failure
and parse_herits (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Kwd Extends, _)) ->
      (Stream.junk __strm;
       let p =
         (try parse_class_path __strm
          with | Stream.Failure -> raise (Stream.Error "")) in
       let l =
         (try parse_herits __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in (HExtends p) :: l)
  | Some ((Kwd Implements, _)) ->
      (Stream.junk __strm;
       let p =
         (try parse_class_path __strm
          with | Stream.Failure -> raise (Stream.Error "")) in
       let l =
         (try parse_other_implements __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in (HImplements p) :: l)
  | _ -> []
and parse_other_implements (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Sep, _)) ->
      (Stream.junk __strm;
       let p =
         (try parse_class_path __strm
          with | Stream.Failure -> raise (Stream.Error "")) in
       let l =
         (try parse_other_implements __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in (HImplements p) :: l)
  | _ -> parse_herits __strm
and parse_class_flags (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Kwd Intrinsic, _)) ->
      (Stream.junk __strm;
       let l =
         (try parse_class_flags __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in HIntrinsic :: l)
  | Some ((Kwd Dynamic, _)) ->
      (Stream.junk __strm;
       let l =
         (try parse_class_flags __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in HDynamic :: l)
  | _ -> []

and parse_field_flags stat pub (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Kwd Static, _)) when stat = IsMember ->
      (Stream.junk __strm;
       (try parse_field_flags IsStatic pub __strm
        with | Stream.Failure -> raise (Stream.Error "")))
  | Some ((Kwd Public, _)) when pub = None ->
      (Stream.junk __strm;
       (try parse_field_flags stat (Some IsPublic) __strm
        with | Stream.Failure -> raise (Stream.Error "")))
  | Some ((Kwd Private, _)) when pub = None ->
      (Stream.junk __strm;
       (try parse_field_flags stat (Some IsPrivate) __strm
        with | Stream.Failure -> raise (Stream.Error "")))
  | _ -> (stat, (match pub with | None -> IsPublic | Some p -> p))           
and parse_args (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Const (Ident name), _)) ->
      (Stream.junk __strm;
       let t =
         (try parse_type_option __strm
          with | Stream.Failure -> raise (Stream.Error "")) in
       let (al, p) =
         (try parse_args2 __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in (((name, t) :: al), p))
  | Some ((PClose, p)) -> (Stream.junk __strm; ([], p))
  | _ -> raise Stream.Failure

and parse_eval_next eval e (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((BkOpen, _)) ->
      (Stream.junk __strm;
       let e2 =
         (try eval __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in
         (match Stream.peek __strm with
          | Some ((BkClose, p2)) ->
              (Stream.junk __strm;
               (try
                  parse_eval_next eval ((EArray (e, e2)), (punion (pos e) p2))
                    __strm
                with | Stream.Failure -> raise (Stream.Error "")))
          | _ -> raise (Stream.Error "")))
  | Some ((Binop op, _)) ->
      (Stream.junk __strm;
       let e2 =
         (try eval __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in make_binop op e e2)
  | Some ((Kwd And, _)) ->
      (Stream.junk __strm;
       let e2 =
         (try eval __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in make_binop OpBoolAnd e e2)
  | Some ((Dot, _)) ->
      (Stream.junk __strm;
       (match Stream.peek __strm with
        | Some ((Const (Ident field), p2)) ->
            (Stream.junk __strm;
             (try
                parse_eval_next eval ((EField (e, field)), (punion (pos e) p2))
                  __strm
              with | Stream.Failure -> raise (Stream.Error "")))
        | _ -> raise (Stream.Error "")))
  | Some ((POpen, _)) ->
      (Stream.junk __strm;
       let args =
         (try parse_eval_list eval __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in
         (match Stream.peek __strm with
          | Some ((PClose, p2)) ->
              (Stream.junk __strm;
               (try
                  parse_eval_next eval ((ECall (e, args)), (punion (pos e) p2))
                    __strm
                with | Stream.Failure -> raise (Stream.Error "")))
          | _ -> raise (Stream.Error "")))
  | Some ((Unop op, p2)) when is_postfix e op ->
      (Stream.junk __strm;
       (try
          parse_eval_next eval ((EUnop (op, Postfix, e)), (punion (pos e) p2))
            __strm
        with | Stream.Failure -> raise (Stream.Error "")))
  | Some ((Question, _)) ->
      (Stream.junk __strm;
       let v1 =
         (try eval __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in
         (match Stream.peek __strm with
          | Some ((DblDot, _)) ->
              (Stream.junk __strm;
               let v2 =
                 (try eval __strm
                  with | Stream.Failure -> raise (Stream.Error ""))
               in
                 (try
                    parse_eval_next eval
                      ((EQuestion (e, v1, v2)), (punion (pos e) (pos v2)))
                      __strm
                  with | Stream.Failure -> raise (Stream.Error "")))
          | _ -> raise (Stream.Error "")))
  | Some ((Kwd InstanceOf, p)) ->
      (Stream.junk __strm;
       let v =
         (try eval __strm
          with | Stream.Failure -> raise (Stream.Error "")) in
       let s = __strm in
       let iof v =
         ((ECall (((EConst (Ident "instanceof")), p), [ e; v ])),
          (punion (pos e) (pos v))) in
       let rec loop =
         (function
          | (EBinop (op, e1, e2), pv) ->
              ((EBinop (op, loop e1, e2)), (punion p pv))
          | (EQuestion (e, e1, e2), pv) ->
              ((EQuestion (loop e, e1, e2)), (punion p pv))
          | v -> iof v)
       in parse_eval_next eval (loop v) s)
  | _ -> e

and parse_field_list eval (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Const (Ident fname), _)) ->
      (Stream.junk __strm;
       (match Stream.peek __strm with
        | Some ((DblDot, _)) ->
            (Stream.junk __strm;
             let e =
               (try eval __strm
                with | Stream.Failure -> raise (Stream.Error "")) in
             let (el, p) =
               (try parse_field_list2 eval __strm
                with | Stream.Failure -> raise (Stream.Error ""))
             in (((fname, e) :: el), p))
        | _ -> raise (Stream.Error "")))
  | Some ((BrClose, p)) -> (Stream.junk __strm; ([], p))
  | _ -> raise Stream.Failure
and parse_field_list2 eval (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Sep, _)) ->
      (Stream.junk __strm;
       (match Stream.peek __strm with
        | Some ((Const (Ident fname), _)) ->
            (Stream.junk __strm;
             (match Stream.peek __strm with
              | Some ((DblDot, _)) ->
                  (Stream.junk __strm;
                   let e =
                     (try eval __strm
                      with | Stream.Failure -> raise (Stream.Error "")) in
                   let (el, p) =
                     (try parse_field_list2 eval __strm
                      with | Stream.Failure -> raise (Stream.Error ""))
                   in (((fname, e) :: el), p))
              | _ -> raise (Stream.Error "")))
        | _ -> raise (Stream.Error "")))
  | Some ((BrClose, p)) -> (Stream.junk __strm; ([], p))
  | _ -> raise Stream.Failure

and parse_array eval (__strm : _ Stream.t) =
  match try Some (eval __strm) with | Stream.Failure -> None with
  | Some e ->
      let (el, p) =
        (try parse_array2 eval __strm
         with | Stream.Failure -> raise (Stream.Error ""))
      in ((e :: el), p)
  | _ ->
      (match Stream.peek __strm with
       | Some ((BkClose, p)) -> (Stream.junk __strm; ([], p))
       | _ -> raise Stream.Failure)
and parse_array2 eval (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Sep, _)) ->
      (Stream.junk __strm;
       let e =
         (try eval __strm
          with | Stream.Failure -> raise (Stream.Error "")) in
       let (el, p) =
         (try parse_array2 eval __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in ((e :: el), p))
  | Some ((BkClose, p)) -> (Stream.junk __strm; ([], p))
  | _ -> raise Stream.Failure
and parse_else expr p (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Next, _)) ->
      (Stream.junk __strm;
       (try parse_else expr p __strm
        with | Stream.Failure -> raise (Stream.Error "")))
  | Some ((Kwd Else, _)) ->
      (Stream.junk __strm;
       let e =
         (try expr __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in ((Some (wrap_var e)), (pos e)))
  | _ -> (None, p)

and parse_finally expr (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Kwd Finally, _)) ->
      (Stream.junk __strm;
       (try Some (expr __strm)
        with | Stream.Failure -> raise (Stream.Error "")))
  | _ -> None
  
and parse_eval_option eval p (__strm : _ Stream.t) =
  match try Some (eval __strm) with | Stream.Failure -> None with
  | Some v -> ((Some v), (pos v))
  | _ -> (None, p)

and parse_eval_list eval (__strm : _ Stream.t) =
  match try Some (eval __strm) with | Stream.Failure -> None with
  | Some v ->
      let vl =
        (try parse_eval_list2 eval __strm
         with | Stream.Failure -> raise (Stream.Error ""))
      in v :: vl
  | _ ->
      (match Stream.peek __strm with
       | Some ((Next, _)) -> (Stream.junk __strm; [])
       | _ -> [])
and parse_eval_list2 eval (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Sep, _)) ->
      (Stream.junk __strm;
       let v =
         (try eval __strm
          with | Stream.Failure -> raise (Stream.Error "")) in
       let vl =
         (try parse_eval_list2 eval __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in v :: vl)
  | Some ((Next, _)) -> (Stream.junk __strm; [])
  | _ -> []

and parse_expr_opt expr (__strm : _ Stream.t) =
  try expr __strm
  with
  | Stream.Failure ->
      (match Stream.peek __strm with
       | Some ((Next, p)) -> (Stream.junk __strm; ((EBlock []), p))
       | _ -> raise Stream.Failure)

and parse_for_conds expr (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Sep, _)) ->
      (Stream.junk __strm;
       let e =
         (try expr __strm
          with | Stream.Failure -> raise (Stream.Error "")) in
       let l =
         (try parse_for_conds expr __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in e :: l)
  | Some ((Next, _)) -> (Stream.junk __strm; [])
  | _ -> []

and parse_vars eval p (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Const (Ident name), p)) ->
      (Stream.junk __strm;
       let t =
         (try parse_type_option __strm
          with | Stream.Failure -> raise (Stream.Error "")) in
       let v =
         (try parse_var_init eval __strm
          with | Stream.Failure -> raise (Stream.Error "")) in
       let (vl, p) =
         (try parse_vars_next eval p __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in (((name, t, v) :: vl), p))
  | _ -> ([], p)

and parse_vars_next eval p (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Sep, _)) ->
      (Stream.junk __strm;
       let (vl, p) =
         (try parse_vars eval p __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in (vl, p))
  | _ -> ([], p)

and parse_var_init eval (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Binop OpAssign, _)) ->
      (Stream.junk __strm;
       (try Some (eval __strm)
        with | Stream.Failure -> raise (Stream.Error "")))
  | _ -> None

and parse_switch_clause expr (__strm : _ Stream.t) =
  match try Some (expr __strm) with | Stream.Failure -> None with
  | Some e ->
      let el =
        (try parse_switch_clause expr __strm
         with | Stream.Failure -> raise (Stream.Error ""))
      in e :: el
  | _ ->
      (match Stream.peek __strm with
       | Some ((Next, _)) ->
           (Stream.junk __strm;
            (try parse_switch_clause expr __strm
             with | Stream.Failure -> raise (Stream.Error "")))
       | _ -> [])  
and parse_expr_list expr p (__strm : _ Stream.t) =
  match try Some (expr __strm) with | Stream.Failure -> None with
  | Some e ->
      let (el, p) =
        (try parse_expr_list expr (pos e) __strm
         with | Stream.Failure -> raise (Stream.Error ""))
      in ((e :: el), p)
  | _ ->
      (match Stream.peek __strm with
       | Some ((Next, _)) ->
           (Stream.junk __strm;
            (try parse_expr_list expr p __strm
             with | Stream.Failure -> raise (Stream.Error "")))
       | _ -> ([], p))
and parse_args2 (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Sep, _)) ->
      (Stream.junk __strm;
       (match Stream.peek __strm with
        | Some ((Const (Ident name), _)) ->
            (Stream.junk __strm;
             let t =
               (try parse_type_option __strm
                with | Stream.Failure -> raise (Stream.Error "")) in
             let (al, p) =
               (try parse_args2 __strm
                with | Stream.Failure -> raise (Stream.Error ""))
             in (((name, t) :: al), p))
        | _ -> raise (Stream.Error "")))
  | Some ((PClose, p)) -> (Stream.junk __strm; ([], p))
  | _ -> raise Stream.Failure
and parse_block callb sp (__strm : _ Stream.t) =
  match try Some (callb __strm) with | Stream.Failure -> None with
  | Some e ->
      let (el, p) =
        (try parse_block callb sp __strm
         with | Stream.Failure -> raise (Stream.Error ""))
      in ((e :: el), p)
  | _ ->
      (match Stream.peek __strm with
       | Some ((Next, _)) ->
           (Stream.junk __strm;
            (try parse_block callb sp __strm
             with | Stream.Failure -> raise (Stream.Error "")))
       | Some ((BrClose, p)) -> (Stream.junk __strm; ([], p))
       | Some ((Eof, _)) ->
           (Stream.junk __strm; error Unclosed_parenthesis sp)
       | _ -> raise Stream.Failure)
and parse_type_option (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((DblDot, _)) ->
      (Stream.junk __strm;
       (try Some (parse_class_path __strm)
        with | Stream.Failure -> raise (Stream.Error "")))
  | _ -> None
and parse_class_path s =
  (last_comment := None;
   let (__strm : _ Stream.t) = s
   in
     match Stream.peek __strm with
     | Some ((Const (Ident "Array"), _)) ->
         (Stream.junk __strm;
          let s = __strm
          in
            (match !last_comment with
             | None -> parse_class_path2 "Array" s
             | Some s -> ([ "#" ^ s ], "Array")))
     | Some ((Const (Ident name), _)) ->
         (Stream.junk __strm;
          (try parse_class_path2 name __strm
           with | Stream.Failure -> raise (Stream.Error "")))
     | _ -> raise Stream.Failure)
and parse_class_path2 name (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Dot, _)) ->
      (Stream.junk __strm;
       let (p, n) =
         (try parse_class_path __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in ((name :: p), n))
  | _ -> ([], name)
and parse_import (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Const (Ident name), _)) ->
      (Stream.junk __strm;
       (try parse_import2 name __strm
        with | Stream.Failure -> raise (Stream.Error "")))
  | Some ((Binop OpMult, _)) -> (Stream.junk __strm; ([], None))
  | _ -> raise Stream.Failure
and parse_import2 name (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Dot, _)) ->
      (Stream.junk __strm;
       let (p, n) =
         (try parse_import __strm
          with | Stream.Failure -> raise (Stream.Error ""))
       in ((name :: p), n))
  | _ -> ([], (Some name))
and parse_metadata (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((BkClose, _)) -> (Stream.junk __strm; ())
  | Some _ ->
      (Stream.junk __strm;
       (try parse_metadata __strm
        with | Stream.Failure -> raise (Stream.Error "")))
  | _ -> raise Stream.Failure
and parse_include (__strm : _ Stream.t) =
  match Stream.peek __strm with
  | Some ((Sharp, p1)) ->
      (Stream.junk __strm;
       (match Stream.peek __strm with
        | Some ((Const (Ident "include"), _)) ->
            (Stream.junk __strm;
             (match Stream.peek __strm with
              | Some ((Const (String inc), p2)) ->
                  (Stream.junk __strm;
                   let t = "ComponentVersion.as" in
                   let tl = String.length t
                   in
                     if
                       ((String.length inc) < tl) ||
                         ((String.sub inc ((String.length inc) - tl) tl) <> t)
                     then !warning "unsupported #include" (punion p1 p2)
                     else ())
              | _ -> raise (Stream.Error "")))
        | _ -> raise (Stream.Error "")))
  | _ -> raise Stream.Failure